package cn.gcBlock.service.impl;

import cn.gcBlock.service.BlockNodeService;
import cn.gcBlock.utils.BlockEncryptUtils;
import cn.gcBlock.utils.PageUtil;
import cn.hutool.log.StaticLog;
import com.alibaba.fastjson.JSON;
import cn.gcBlock.exception.BadRequestException;
import cn.gcBlock.po.Node;
import cn.gcBlock.po.NodeEntity;
import cn.gcBlock.po.constant.NodeType;
import cn.gcBlock.repository.NodeRepository;
import cn.gcBlock.dto.NewsQueryCriteria;
import cn.gcBlock.dto.NodeDTO;
import cn.gcBlock.dto.NodeQueryCriteria;
import cn.gcBlock.service.mapper.BlockNodeMapper;
import cn.gcBlock.utils.QueryHelp;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class BlockNodeServiceImpl implements BlockNodeService {
    @Resource
    NodeRepository nodeRepository;
    @Resource
    private BlockNodeMapper blockNodeMapper;

    //保存区块
    @Override
    public NodeDTO create(NodeDTO node) {
        return blockNodeMapper.toDto(nodeRepository.saveAndFlush(blockNodeMapper.toEntity(node)));
    }

    @Override
    //获取余额
    public double getRemainingSum(String publicKey) {
        if (StringUtils.isEmpty(publicKey)) {
            throw new BadRequestException("publicKey is null!");
        }
        double sum = 0;
        try{
        Long getMoney = nodeRepository.sumGetMoney(publicKey);
        Long payMoney = nodeRepository.sumPayMoney(publicKey);
        StaticLog.info("getMoney={},payMoney={}", getMoney, payMoney);
        if (getMoney == null) {
            getMoney = new Long(0);
        }
        if (payMoney == null) {
            payMoney = new Long(0);
        }
        sum = (getMoney - payMoney);
        return sum;
        }catch (Exception e){
            return 0;
        }
    }

    //查询钱包是否存在
    @Override
    public boolean isExistBlock(String blockAddress, String symbol) {
        long count = nodeRepository.countByPublicKeyAndTypeAndSymbol(blockAddress, NodeType.WALLET.getCode(), symbol);
        if (count > 0) {
            return true;
        } else {
            return false;
        }
    }

    /***
     * 是否是服务者
     * @param publicKey
     * @return
     */
    @Override
    public boolean isPublicServer(String publicKey) {
        int sumGua = nodeRepository.sumGua(publicKey);
        int sumCon = nodeRepository.sumCon(publicKey);
        if(sumGua-sumCon > 0){
            return true;
        }
        return false;
    }

    @Override
    public List<NodeDTO> findWallet(String publicKey) {
        List<Node> nodeList = nodeRepository.findByPublicKeyAndTypeOrderByTimestampDesc(publicKey, NodeType.WALLET.getCode());
        return blockNodeMapper.toDto(nodeList);
    }

    @Override
    public List<String> findSymbols() {
        return nodeRepository.findSymbols();
    }

    @Override
    public NodeDTO findCreation(String symbol) {
        return blockNodeMapper.toDto(nodeRepository.findByTypeAndSymbol(NodeType.CREATION.getCode(), symbol));
    }

    @Override
    public boolean isExistCreationBlock(String symbol) {
        long count = nodeRepository.countByTypeAndSymbol(NodeType.CREATION.getCode(), symbol);
        if (count > 1) {
            throw new BadRequestException("此区块链系统错误，请选择其他服务区块");
        }
        if (count == 1) {
            return true;
        }
        if (count < 1) {
            return false;
        }
        return false;
    }

    @Override
    @Transactional
    public NodeDTO registerBlockAddress(String symbol) {
        NodeDTO node = new NodeDTO();
        Map<String, String> map = BlockEncryptUtils.generateKeyPair();
        node.setPrivateKey(map.get("privateKey"));
        node.setPublicKey(map.get("publicKey"));
        node.setSymbol(symbol);
        return node;
    }

    @Override
    public List<NodeDTO> findAll() {
        return blockNodeMapper.toDto(nodeRepository.findAll());
    }

    @Override
    public NodeDTO sign(NodeDTO resource) {
        String privateKey = resource.getPrivateKey();
        NodeEntity nodeEntity = new NodeEntity();
        BeanUtils.copyProperties(resource, nodeEntity);
        String entityJSON = JSON.toJSONString(nodeEntity);
        String signature;
        try {
            signature = BlockEncryptUtils.sign(entityJSON, privateKey);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BadRequestException("私钥错误");
        }

        boolean boo = BlockEncryptUtils.verify(entityJSON, signature, nodeEntity.getPublicKey());
        if (!boo) {
            throw new BadRequestException("钱包地址与私钥不匹配");
        }
        resource.setSignature(signature);
        return resource;
    }

    @Override
    public Object queryAll(NodeQueryCriteria criteria, Pageable pageable) {
        Page<Node> page = nodeRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(blockNodeMapper::toDto));
    }

    @Override
    public Object queryAll(NewsQueryCriteria criteria, Pageable pageable) {
        Page<Node> page = nodeRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return PageUtil.toPage(page.map(blockNodeMapper::toDto));
    }

    @Override
    public Object queryAll(NodeQueryCriteria criteria) {
        return blockNodeMapper.toDto(nodeRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    public NodeDTO findById(String signature) {
        Optional<Node> blockNode = nodeRepository.findById(signature);
        if(!blockNode.isPresent()){
            throw new BadRequestException("未查询到对应区块");
        }
        return blockNodeMapper.toDto(blockNode.get());
    }

    @Override
    public NodeDTO findOneByCriteria(NodeQueryCriteria criteria) {
        Optional<Node> optionalNode = nodeRepository.findOne((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        if (optionalNode.isPresent()) {
            return blockNodeMapper.toDto(optionalNode.get());
        } else {
            return null;
        }


    }

    @Override
    public Page<Node> queryNodes(NodeQueryCriteria criteria, Pageable pageable) {
        Page<Node> page = nodeRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        return page;
    }
}
